package com.hhd.pdf.components;

import com.alibaba.fastjson.JSONObject;
import com.hhd.pdf.components.branch.HasBranch;
import com.hhd.pdf.components.table.TrVo;
import com.hhd.pdf.context.GeneratorContext;
import com.hhd.pdf.variable.*;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Phrase;
import com.hhd.pdf.exceptions.PdfException;
import com.hhd.pdf.CommonUtils;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.tree.DefaultElement;
import org.dom4j.tree.DefaultText;

import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

public abstract class AbstractPdfComponent implements PdfComponent {

    public List<com.itextpdf.text.Element> parseSimpleNode(Element element, GeneratorContext context)
            throws DocumentException, IOException {
        List<Node> nodes = element.content();

        List<com.itextpdf.text.Element> elements = new LinkedList<>();
        for (Node node : nodes) {
            if (node instanceof DefaultText) {
                DefaultText text = (DefaultText) node;
                String value = parseSimpleFormula(text.getText(), context);
                elements.addAll(createPhrase(value, context));
            } else {
                if (!has((DefaultElement) node, context)) continue;
                InLineComponent component = getInLineComponent(node);

                if (component instanceof InLineBlockComponent) {
                    elements.addAll(createInlineBlock((DefaultElement) node, context.createRootVariableContext(), (InLineBlockComponent) component, context));
                    continue;
                }

                String formula = getFormula(context, (DefaultElement) node, component);
                elements.addAll(component.createPdfElement((DefaultElement) node, context, formula));
            }
        }
        return elements;
    }

    protected String getFormulaStringValue(String formulaText, VariableContext rootVariableContext) {
        CompositeFormula formula = CompositeFormula.formula(formulaText);
        formula.execute(rootVariableContext);
        return formula.joinFormulas();
    }

    private String getFormula(GeneratorContext context, DefaultElement node, InLineComponent component) {
        String formula = component.getFormula(node);
        return parseSimpleFormula(formula, context);
    }

    protected List<ParsedFormula> getFormulas(Element element) {
        List<CompositeFormula> elementFormulas = getElementFormulas(element);
        LinkedList<ParsedFormula> parsedFormulas = new LinkedList<>();
        for (CompositeFormula elementFormula : elementFormulas) {
            parsedFormulas.addAll(elementFormula.getFormulas());
        }
        return parsedFormulas;
    }
    protected List<CompositeFormula> getElementFormulas(Element element) {
        LinkedList<CompositeFormula> compositeFormulas = new LinkedList<>();
        List<Node> nodes = element.content();
        for (Node node : nodes) {
            if (node instanceof DefaultText) {
                DefaultText text = (DefaultText) node;
                compositeFormulas.add(CompositeFormula.formula(text.getText()));
            } else {
                InLineComponent component = getInLineComponent(node);

                String formula = component.getFormula((DefaultElement) node);
                compositeFormulas.add(CompositeFormula.formula(formula));
            }
        }
        return compositeFormulas;
    }

//    protected List<CompositeFormula> getElementFormulas(Element element) {
//        LinkedList<CompositeFormula> compositeFormulas = new LinkedList<>();
//        List<Node> nodes = element.content();
//        for (Node node : nodes) {
//            if (node instanceof DefaultText) {
//                DefaultText text = (DefaultText) node;
//                compositeFormulas.add(CompositeFormula.formula(text.getText()));
//            } else {
//                InLineComponent component = getInLineComponent(node);
//
//                String formula = component.getFormula((DefaultElement) node);
//                compositeFormulas.add(CompositeFormula.formula(formula));
//            }
//        }
//        return compositeFormulas;
//    }

    protected List<com.itextpdf.text.Element> createPdfElementsByVariableContext(VariableContext variableContext,
                                                                                 Element element,
                                                                                 GeneratorContext context) throws DocumentException, IOException {
        List<com.itextpdf.text.Element> elements = new LinkedList<>();
        List<Node> nodes = element.content();
        for (Node node : nodes) {
            if (node instanceof DefaultText) {
                DefaultText text = (DefaultText) node;
                if (CommonUtils.isEmpty(text.getText().trim())) continue;
                CompositeFormula formula = getExecutedCompositeFormula(text.getText(), variableContext);
                elements.addAll(createPhrase(formula.joinFormulas(), context));
            } else {
                if (!has((DefaultElement) node, variableContext)) continue;
                InLineComponent component = getInLineComponent(node);
                if (component instanceof InLineBlockComponent) {
                    elements.addAll(createInlineBlock((DefaultElement) node, variableContext, (InLineBlockComponent) component, context));
                    continue;
                }
                CompositeFormula formula = getExecutedCompositeFormula(component.getFormula((DefaultElement) node), variableContext);
                elements.addAll(component.createPdfElement((DefaultElement) node, context, formula.joinFormulas()));
            }
        }
        return elements;
    }

    private static CompositeFormula getExecutedCompositeFormula(String component, VariableContext variableContext) {
        CompositeFormula formula = CompositeFormula.formula(component.trim());
        formula.execute(variableContext);
        return formula;
    }

    private static List<com.itextpdf.text.Element> createInlineBlock(Element element, VariableContext variableContext, InLineBlockComponent component, GeneratorContext context) throws DocumentException, IOException {
        return component.createElements(element, context, variableContext);
    }

    protected ParsedFormula getMaxDeepFormula(List<ParsedFormula> formulas, TrVo tr) {
        try {
            String list = tr.getTr().attributeValue("list");
            if (!CommonUtils.isEmpty(list)) {
                return ParsedFormula.formula(list);
            }
        } catch (Exception e) {
        }
        int deep = -1;
        ParsedFormula res = null;
        for (ParsedFormula formula : formulas) {
            formula.createVariableFormula();
            if (deep < formula.getVariablePath().getDeep()) {
                deep = formula.getVariablePath().getDeep();
                res = formula;
            }
        }
        return res;
    }

    private Collection<? extends com.itextpdf.text.Element> createPhrase(String value, GeneratorContext context) {
        String[] split = value.split("\\n");
        LinkedList<com.itextpdf.text.Element> elements = new LinkedList<>();
        for (int i = 0; i < split.length; i++) {
            elements.add(new Phrase(resolveBlankPhrase(split[i]), context.getGeneratorParameters().getFont()));
            if (i != split.length - 1) {
                elements.add(new Phrase("\n"));
            }
        }
        return elements;
    }

    private static String resolveBlankPhrase(String string) {
        if (CommonUtils.isEmpty(string)) return " ";
        return string;
    }

    private InLineComponent getInLineComponent(Node node) {
        InLineComponent component = InlineComponentFinder.getComponent(node.getName());
        if (CommonUtils.isEmpty(component)) throw new PdfException("不支持的行内组件: " + node.getName());
        return component;
    }

    private String parseSimpleFormula(String formula, GeneratorContext context) {
        JSONObject renderData = context.getGeneratorParameters().getRenderData();
        SimpleVariableParser parser = SimpleVariableParser.builder()
                .renderData(renderData)
                .formula(formula)
                .build();
        return parser.parse();
    }

    public boolean has(Element element, VariableContext variableContext) {
        try {
//            Attribute has = element.attribute("has");
//            String text = has.getText();
//            if(CommonUtils.isEmpty(text)) return true;
//            return variableContext.has(text);
            return HasBranch.has(element, variableContext);
        } catch (Exception ignored) {
        }
        return true;
    }
    @Override
    public boolean has(Element element, GeneratorContext context) {
        return has(element, context.createRootVariableContext());
    }
}
